#include "ClientSocket.h"
#include <QTimer>

ClientSocket::ClientSocket(QTcpSocket* sock) : m_socket(sock)
{
    m_size = 0;
    connect(m_socket, SIGNAL(readyRead()), this, SLOT(dataReceived()));

    QTimer *timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(send()));
    timer->start(10);

    qDebug() << "Created";
}

void ClientSocket::send()
{
    if(m_toSend.isEmpty())
        return;
    if(m_socket->bytesToWrite() > 0)
        return;

    Data *d = m_toSend.dequeue();
    while(1)
    {
        if(!d->isTooLate())
        {
            m_socket->write(d->data);
            qDebug() << "Sending...";
            break;
        }
        if(m_toSend.isEmpty())
            break;
        delete d;
        d = m_toSend.dequeue();
    }
    delete d;
}


void ClientSocket::dataReceived()
{
    QDataStream in(m_socket);

    if (m_size == 0)
    {
        if (m_socket->bytesAvailable() < (int)sizeof(quint32))
             return;

        in >> m_size;
    }

    if (m_socket->bytesAvailable() < m_size)
        return;
    quint32 r;
    in >> r;
    int req = r;

    switch (req)
    {
        case REQUEST_NEXT_PHASE:
        {
            emit requestNextPhase();
        }
        break;
        case REQUEST_UPDATE:
        {
            emit requestUpdate();
        }
        break;
        case REQUEST_PLAYER_INFOS:
        {
            quint32 ply;
            in >> ply;
            emit requestPlayerInfos(ply);
        }
        break;
        case REQUEST_STATE_INFOS:
        {
            QString state;
            in >> state;
            emit requestStateInfos(state);
        }
        break;
        case REQUEST_MOVEMENT:
        {
            QString from; QString to; quint32 nbr;
            in >> from >> to >> nbr;
            emit requestMovement(from, to, nbr);
        }
        break;
        case REQUEST_ATTACK:
        {
            QString from; QString to; quint32 nbr; bool a;
            in >> from >> to >> nbr >> a;
            emit requestAttack(from, to, nbr, a);
            qDebug() << "Attack requested : " << from << " " << to << " " << nbr << " " << a;
        }
        break;
        case REQUEST_REINFORCEMENT:
        {
            QString state; quint32 nbr;
            in >> state >> nbr;
            emit requestReinforcement(state, nbr);
        }
        break;
        case REQUEST_LOBBY_STATE_CHANGE:
        {
            emit requestChangeLobbyState();
        }
        break;
        default:
            qDebug() << "Wtf detected.";
            QByteArray bin;
            in >> bin;
        break;
    }

    qDebug() << "Data received and read.";

    m_size = 0;
}


namespace {

void ClientSocket::phaseChanged(PHASE phase, int player)
{
    Data* d = new Data;
    QDataStream out(&d->data, QIODevice::WriteOnly);

    out << (quint32) 0;
    out << (quint32) PHASE_CHANGED;
    out << (quint32) phase;
    out << (quint32) player;
    out.device()->seek(0);
    out << (quint32) (d->data.size() - sizeof(quint32));
    m_toSend.enqueue(d);
}

void ClientSocket::changeLobbyState(bool ready)
{
    Data* d = new Data;
    QDataStream out(&d->data, QIODevice::WriteOnly);

    out << (quint32) 0;
    out << (quint32) LOBBY_STATE_CHANGED;
    out << ready;
    out.device()->seek(0);
    out << (quint32) (d->data.size() - sizeof(quint32));
    m_toSend.enqueue(d);
}

void ClientSocket::playerInfos(PlayerInfos infos)
{
    Data* d = new Data;
    QDataStream out(&d->data, QIODevice::WriteOnly);

    out << (quint32) 0;
    out << (quint32) PLAYER_INFOS;
    out << (PlayerInfos) infos;
    out.device()->seek(0);
    out << (quint32) (d->data.size() - sizeof(quint32));
    m_toSend.enqueue(d);
}

void ClientSocket::stateInfos(StateInfos infos)
{
    Data* d = new Data;
    QDataStream out(&d->data, QIODevice::WriteOnly);

    out << (quint32) 0;
    out << (quint32) STATE_INFOS;
    out << (StateInfos) infos;
    out.device()->seek(0);
    out << (quint32) (d->data.size() - sizeof(quint32));
    m_toSend.enqueue(d);
}

void ClientSocket::allStatesInfos(QList<StateInfos> infos)
{
    Data* d = new Data;
    QDataStream out(&d->data, QIODevice::WriteOnly);

    out << (quint32) 0;
    out << (quint32) ALL_STATES_INFOS;
    out << infos;
    out.device()->seek(0);
    out << (quint32) (d->data.size() - sizeof(quint32));
    m_toSend.enqueue(d);
}

void ClientSocket::allPlayersInfos(QList<PlayerInfos> infos)
{
    Data* d = new Data;
    QDataStream out(&d->data, QIODevice::WriteOnly);

    out << (quint32) 0;
    out << (quint32) ALL_PLAYERS_INFOS;
    out << infos;
    out.device()->seek(0);
    out << (quint32) (d->data.size() - sizeof(quint32));
    m_toSend.enqueue(d);
}

void ClientSocket::error(ERROR_CODE err, QStringList arg)
{
    Data* d = new Data;
    QDataStream out(&d->data, QIODevice::WriteOnly);

    out << (quint32) 0;
    out << (quint32) ERROR;
    out << (quint32) err;
    out << arg;
    out.device()->seek(0);
    out << (quint32) (d->data.size() - sizeof(quint32));
    m_toSend.enqueue(d);
}

void ClientSocket::info(INFOS_CODE inf, QStringList arg)
{
    Data* d = new Data;
    QDataStream out(&d->data, QIODevice::WriteOnly);

    out << (quint32) 0;
    out << (quint32) INFOS;
    out << (quint32) inf;
    out << arg;
    out.device()->seek(0);
    out << (quint32) (d->data.size() - sizeof(quint32));
    m_toSend.enqueue(d);
}

void ClientSocket::requestDiceRoll(bool defensor)
{
    Data* d = new Data;
    QDataStream out(&d->data, QIODevice::WriteOnly);

    out << (quint32) 0;
    out << (quint32) REQUEST_DICE_ROLL;
    out << defensor;
    out.device()->seek(0);
    out << (quint32) (d->data.size() - sizeof(quint32));
    m_toSend.enqueue(d);
}

void ClientSocket::playerDefeated(quint32 ID)
{
    Data* d = new Data;
    QDataStream out(&d->data, QIODevice::WriteOnly);

    out << (quint32) 0;
    out << (quint32) PLAYER_DEFEATED;
    out << (quint32) ID;
    out.device()->seek(0);
    out << (quint32) (d->data.size() - sizeof(quint32));
    m_toSend.enqueue(d);
}

void ClientSocket::gameStarted(quint32 nbr)
{
    Data* d = new Data;
    QDataStream out(&d->data, QIODevice::WriteOnly);

    out << (quint32) 0;
    out << (quint32) GAME_STARTED;
    out << (quint32) nbr;
    out.device()->seek(0);
    out << (quint32) (d->data.size() - sizeof(quint32));
    m_toSend.enqueue(d);
}

}
